﻿/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel;

namespace IvyOrm.Test
{
    [TestClass]
    public class ValueConverterFactoryTest
    {
        class UnconvertableClass
        { }
        struct UnconvertableStruct
        { }

        [TestMethod]
        [TestCategory("No Database")]
        public void TryGetConverter_NullArgument_ThrowsException()
        {
            Converter<object, object> converter;
            var converterFactory = new ValueConverterFactory();
            AssertB.ThrowsException<ArgumentNullException>(() => converterFactory.TryGetConverter(null, typeof(string), out converter));
            AssertB.ThrowsException<ArgumentNullException>(() => converterFactory.TryGetConverter(typeof(string), null, out converter));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void TryGetConverter_UnconvertableTypes_ReturnsFalse()
        {
            Converter<object, object> converter;

            var converterFactory = new ValueConverterFactory();
            Assert.AreEqual(false, converterFactory.TryGetConverter(typeof(string), typeof(UnconvertableClass), out converter));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_NullArgument_ThrowsException()
        {
            var converterFactory = new ValueConverterFactory();
            AssertB.ThrowsException<ArgumentNullException>(() => converterFactory.GetConverter(null, typeof(string)));
            AssertB.ThrowsException<ArgumentNullException>(() => converterFactory.GetConverter(typeof(string), null));
        }

        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_UnconvertableTypes_ThrowsException()
        {
            var converterFactory = new ValueConverterFactory();
            AssertB.ThrowsException<NotSupportedException>(() => converterFactory.GetConverter(typeof(string), typeof(UnconvertableClass)));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_SameType()
        {
            {
                ValueConverterFactory factory = new ValueConverterFactory();
                var converter = factory.GetConverter(typeof(int), typeof(int));

                Assert.AreEqual(1, converter(1));
            }
            {
                ValueConverterFactory factory = new ValueConverterFactory();
                var converter = factory.GetConverter(typeof(string), typeof(string));

                Assert.AreEqual("1", converter("1"));
            }

            {
                UnconvertableClass t = new UnconvertableClass();

                ValueConverterFactory factory = new ValueConverterFactory();
                var converter = factory.GetConverter(typeof(UnconvertableClass), typeof(UnconvertableClass));

                Assert.AreEqual(t, converter(t));
            }
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Destination_NullableType()
        {
            ValueConverterFactory factory = new ValueConverterFactory();
            var converter = factory.GetConverter(typeof(UnconvertableStruct), typeof(UnconvertableStruct?));
            UnconvertableStruct v = new UnconvertableStruct();

            Assert.AreEqual(v, converter(v));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Source_Is_Nullable_Compatable_With_Destination()
        {
            ValueConverterFactory factory = new ValueConverterFactory();
            var converter = factory.GetConverter(typeof(UnconvertableStruct?), typeof(UnconvertableStruct));
            UnconvertableStruct v = new UnconvertableStruct();

            Assert.AreEqual(v, converter(v));

            AssertB.ThrowsException<InvalidCastException>(() => converter(null));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Source_Is_Nullable_Incompatible_With_Destination()
        {
            ValueConverterFactory factory = new ValueConverterFactory();
            AssertB.ThrowsException<NotSupportedException>(() =>
                {
                    factory.GetConverter(typeof(UnconvertableStruct?), typeof(UnconvertableClass));
                });
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_BaseType()
        {
            ValueConverterFactory factory = new ValueConverterFactory();
            var converter = factory.GetConverter(typeof(int), typeof(object));

            Assert.AreEqual(1, converter(1));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_ConvertToTypeCompatable()
        {
            ValueConverterFactory factory = new ValueConverterFactory();

            Assert.AreEqual(Convert.ToDouble(1), factory.GetConverter(typeof(int), typeof(double))(1));
            Assert.AreEqual(Convert.ToInt64(1), factory.GetConverter(typeof(int), typeof(Int64))(1));
            Assert.AreEqual(10, factory.GetConverter(typeof(float), typeof(int))((float)10));
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Enum_FromAndToNumber()
        {
            var factory = new ValueConverterFactory();
            
            { //ToEnum (compatible types)
                var converter = factory.GetConverter(typeof(int), typeof(TypeCode));
                Assert.AreEqual(TypeCode.Char, converter((int)TypeCode.Char));
            }

            { //ToEnum (different types)
                var converter = factory.GetConverter(typeof(double), typeof(TypeCode));
                Assert.AreEqual(TypeCode.Char, converter((double)TypeCode.Char));
            }


            {  //FromEnum (compatible types)
                var converter = factory.GetConverter(typeof(TypeCode), typeof(int));
                Assert.AreEqual((int)TypeCode.Char, converter(TypeCode.Char));
            }


            { //FromEnum (different types)
                var converter = factory.GetConverter(typeof(TypeCode), typeof(byte));
                Assert.AreEqual((byte)TypeCode.Char, converter(TypeCode.Char));
            }
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Enum_Incompatible()
        {
            var factory = new ValueConverterFactory();

            AssertB.ThrowsException<NotSupportedException>(() => factory.GetConverter(typeof(UnconvertableClass), typeof(TypeCode)));
            AssertB.ThrowsException<NotSupportedException>(() => factory.GetConverter(typeof(TypeCode), typeof(UnconvertableClass)));
        }
        [TestMethod]
        public void GetConverter_Enum_FromAndToString()
        {
            var factory = new ValueConverterFactory();

            { //ToEnum 
                var converter = factory.GetConverter(typeof(string), typeof(TypeCode));
                Assert.AreEqual(TypeCode.Char, converter(TypeCode.Char.ToString()));
            }

            {  //FromEnum
                var converter = factory.GetConverter(typeof(TypeCode), typeof(string));
                Assert.AreEqual(TypeCode.Char.ToString(), converter(TypeCode.Char));
            }

        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Enum_ToNullableEnum()
        {
            var factory = new ValueConverterFactory();

            {
                var converter = factory.GetConverter(typeof(string), typeof(TypeCode?));
                Assert.AreEqual(TypeCode.Char, converter(TypeCode.Char.ToString()));
            }

            {
                var converter = factory.GetConverter(typeof(string), typeof(TypeCode?));
                Assert.AreEqual(null, converter(null));
            }

            {
                var converter = factory.GetConverter(typeof(int), typeof(TypeCode?));
                Assert.AreEqual(TypeCode.Char, converter((int)TypeCode.Char));
            }

            {
                var converter = factory.GetConverter(typeof(int?), typeof(TypeCode?));
                Assert.AreEqual(TypeCode.Char, converter((int?)TypeCode.Char));
            }

            {
                var converter = factory.GetConverter(typeof(int?), typeof(TypeCode?));
                Assert.AreEqual(null, converter(null));
            }
        }
        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_TypeDescriptor()
        {
            var factory = new ValueConverterFactory();
            { //ToTimeSpan
                var converter = factory.GetConverter(typeof(string), typeof(TimeSpan));
                Assert.AreEqual(new TimeSpan(1, 0, 0), converter((new TimeSpan(1, 0, 0)).ToString()));
            }

            { //FromTimeSpan
                var converter = factory.GetConverter( typeof(TimeSpan), typeof(string));
                Assert.AreEqual((new TimeSpan(1, 0, 0)).ToString(), converter(new TimeSpan(1, 0, 0)));
            }
        }

        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_IncompatibleNullable()
        {
            var factory = new ValueConverterFactory();

            AssertB.ThrowsException<NotSupportedException>(() => factory.GetConverter(typeof(int?), typeof(UnconvertableStruct?)));
            AssertB.ThrowsException<NotSupportedException>(() => factory.GetConverter(typeof(int), typeof(UnconvertableStruct?)));
            AssertB.ThrowsException<NotSupportedException>(() => factory.GetConverter(typeof(int?), typeof(UnconvertableStruct)));
        }

        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_Int64ToNullableInt32()
        {
            var factory = new ValueConverterFactory();
            var converter = factory.GetConverter(typeof(Int64), typeof(Int32?));
            Assert.AreEqual((Int32)1, converter(Convert.ToInt32(1)));
        }

        [TestMethod]
        [TestCategory("No Database")]
        public void GetConverter_ObjectToInt32()
        {
            var factory = new ValueConverterFactory();
            var converter = factory.GetConverter(typeof(object), typeof(Int32));
            Assert.AreEqual((Int32)1, converter(1));
            AssertB.ThrowsException<InvalidCastException>(() => converter(new object()));
        }
    }
}
